package com.clouddisk.web.controller.system;

import cn.hutool.core.bean.BeanUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.clouddisk.common.annotation.Log;
import com.clouddisk.common.core.controller.BaseController;
import com.clouddisk.common.core.domain.R;
import com.clouddisk.common.core.domain.dto.file.*;
import com.clouddisk.common.core.domain.entity.FileBean;
import com.clouddisk.common.core.domain.entity.UserFile;
import com.clouddisk.common.enums.BusinessType;
import com.clouddisk.common.utils.FilesUtils;
import com.clouddisk.common.utils.file.FilesUtil;
import com.clouddisk.common.utils.file.TreeNode;
import com.clouddisk.system.component.AsyncTaskComp;
import com.clouddisk.system.component.FileDealComp;
import com.clouddisk.system.domain.FileSearch;
import com.clouddisk.system.domain.vo.file.FileDetailVO;
import com.clouddisk.system.domain.vo.file.FileListVo;
import com.clouddisk.system.domain.vo.file.SearchFileVO;
import com.clouddisk.system.service.IFileService;
import com.clouddisk.system.service.IUserFileService;
import com.qiwenshare.common.exception.QiwenException;
import com.qiwenshare.common.util.DateUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/**
 * 用户文件管理
 * 该接口为文件接口，主要用来做一些文件的基本操作，如创建目录，删除，移动，复制等
 * @author ZJH
 *
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/file")
public class SysFileController extends BaseController {

    @Resource
    IFileService fileService;
    @Resource
    IUserFileService userFileService;

    @Resource
    FileDealComp fileDealComp;
    @Resource
    AsyncTaskComp asyncTaskComp;
    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Log(title = "创建文件", businessType = BusinessType.INSERT)
    @RequestMapping(value = "/createfile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> createFile(@Valid @RequestBody CreateFileDTO createFileDto) {
        boolean isDirExist = fileDealComp.isDirExist(createFileDto.getFileName(), createFileDto.getFilePath(), getUserId(),createFileDto.getFileDeptName());
        if (isDirExist) {
            return R.fail("同名文件已存在");
        }
        UserFile userFile = FilesUtil.getDir(getUserId(), createFileDto.getFilePath(), createFileDto.getFileName(),createFileDto.getFileDeptName());
        userFileService.save(userFile);
        fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
        return R.ok();
    }

    @Log(title = "文件搜索", businessType = BusinessType.OTHER)
    @GetMapping(value = "/search")
    @ResponseBody
    public R<List<SearchFileVO>> searchFile(SearchFileDTO searchFileDTO) {
        int currentPage = (int)searchFileDTO.getCurrentPage() - 1;
        int pageCount = (int)(searchFileDTO.getPageCount() == 0 ? 10 : searchFileDTO.getPageCount());

        SearchResponse<FileSearch> search = null;
        try {
            if("person".equals(searchFileDTO.getFileDeptName())) {
                search = elasticsearchClient.search(s -> s
                        .index("filesearch")
                        .query(_1 -> _1
                            .bool(_2 -> _2
                                .must(_3 -> _3
                                    .bool(_4 -> _4
                                        .should(_5 -> _5
                                            .match(_6 -> _6
                                                .field("fileName")
                                                .query(searchFileDTO.getFileName())))
                                        .should(_5 -> _5
                                            .wildcard(_6 -> _6
                                                .field("fileName")
                                                .wildcard("*" + searchFileDTO.getFileName() + "*")))
                                        .should(_5 -> _5
                                            .match(_6 -> _6
                                                .field("content")
                                                .query(searchFileDTO.getFileName())))
                                    ))
                                .must(_3 -> _3
                                    .term(_4 -> _4
                                        .field("deleteFlag")
                                        .value(0L)))
                                .must(_3 -> _3
                                    .term(_4 -> _4
                                        .field("userId")
                                        .value(getUserId())))
                            ))
                        .from(currentPage)
                        .size(pageCount)
                        .highlight(h -> h
                            .fields("fileName", f -> f.type("plain")
                                .preTags("<span class='keyword'>").postTags("</span>"))
                            .encoder(HighlighterEncoder.Html))
                    ,FileSearch.class);
            }else{
                search = elasticsearchClient.search(s -> s
                        .index("filesearch")
                        .query(_1 -> _1
                            .bool(_2 -> _2
                                .must(_3 -> _3
                                    .bool(_4 -> _4
                                        .should(_5 -> _5
                                            .match(_6 -> _6
                                                .field("fileName")
                                                .query(searchFileDTO.getFileName())))
                                        .should(_5 -> _5
                                            .wildcard(_6 -> _6
                                                .field("fileName")
                                                .wildcard("*" + searchFileDTO.getFileName() + "*")))
                                        .should(_5 -> _5
                                            .match(_6 -> _6
                                                .field("content")
                                                .query(searchFileDTO.getFileName())))
                                    ))
                                .must(_3 -> _3
                                    .term(_4 -> _4
                                        .field("deleteFlag")
                                        .value(0L)))
                                .must(_3 -> _3
                                    .match(_4 -> _4
                                        .field("fileDeptName")
                                        .query(searchFileDTO.getFileDeptName())))
                            ))
                        .from(currentPage)
                        .size(pageCount)
                        .highlight(h -> h
                            .fields("fileName", f -> f.type("plain")
                                .preTags("<span class='keyword'>").postTags("</span>"))
                            .encoder(HighlighterEncoder.Html))
                    ,FileSearch.class);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        List<SearchFileVO> searchFileVOList = new ArrayList<>();
        for (Hit<FileSearch> hit : search.hits().hits()) {
            SearchFileVO searchFileVO = new SearchFileVO();
            BeanUtil.copyProperties(hit.source(), searchFileVO);
            searchFileVO.setHighLight(hit.highlight());
            searchFileVOList.add(searchFileVO);
            asyncTaskComp.checkESUserFileId(searchFileVO.getUserFileId());
        }
        return R.ok(searchFileVOList);
    }

    @Log(title = "文件重命名", businessType = BusinessType.UPDATE)
    @RequestMapping(value = "/renamefile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> renameFile(@RequestBody RenameFileDTO renameFileDto) {
        UserFile userFile = userFileService.getById(renameFileDto.getUserFileId());

        List<UserFile> userFiles = userFileService.selectUserFileByNameAndPath(renameFileDto.getFileName(), userFile.getFilePath(), getUserId(),userFile.getFileDeptName());
        if (userFiles != null && !userFiles.isEmpty()) {
            return R.fail("同名文件已存在");
        }

        LambdaUpdateWrapper<UserFile> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(UserFile::getFileName, renameFileDto.getFileName())
            .set(UserFile::getUploadTime, DateUtil.getCurrentTime())
            .eq(UserFile::getUserFileId, renameFileDto.getUserFileId())
            .eq(UserFile::getFileDeptName,renameFileDto.getFileDeptName());
        userFileService.update(lambdaUpdateWrapper);
        if (1 == userFile.getIsDir()) {
            List<UserFile> list = userFileService.selectUserFileByLikeRightFilePath(new FilesUtils(userFile.getFilePath(), userFile.getFileName(), true).getPath(), getUserId(),userFile.getFileDeptName());

            for (UserFile newUserFile : list) {
                newUserFile.setFilePath(newUserFile.getFilePath().replaceFirst(new FilesUtils(userFile.getFilePath(), userFile.getFileName(), userFile.getIsDir() == 1).getPath(),
                    new FilesUtils(userFile.getFilePath(), renameFileDto.getFileName(), userFile.getIsDir() == 1).getPath()));
                userFileService.updateById(newUserFile);
            }
        }
        fileDealComp.uploadESByUserFileId(renameFileDto.getUserFileId());
        return R.ok();
    }

    @RequestMapping(value = "/getfilelist", method = RequestMethod.GET)
    @ResponseBody
    public R<Map<String,Object>> getFileList(String filePath,  String fileDeptName, long currentPage, long pageCount){
        IPage<FileListVo> fileList;
        if ("person".equals(fileDeptName)){
            fileList = userFileService.userFileList(getUserId(), fileDeptName,filePath, currentPage, pageCount);
        }else {
            fileList = userFileService.userFileList(null, fileDeptName,filePath, currentPage, pageCount);
        }
        Map<String, Object> map = new HashMap<>();
        map.put("total", fileList.getTotal());
        map.put("list", fileList.getRecords());
        return R.ok(map);
    }

    @Log(title = "批量删除文件", businessType = BusinessType.DELETE)
    @RequestMapping(value = "/batchdeletefile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> deleteImageByIds(@RequestBody BatchDeleteFileDTO batchDeleteFileDto) {
        List<UserFile> userFiles = JSON.parseArray(batchDeleteFileDto.getFiles(), UserFile.class);
        DigestUtils.md5Hex("data");
        for (UserFile userFile : userFiles) {
            userFileService.deleteUserFile(userFile.getUserFileId(),getUserId(),userFile.getFileDeptName());
            fileDealComp.updateESByUserFileId(userFile.getUserFileId(),1);
        }
        return R.ok("批量删除文件成功");
    }

    @Log(title = "删除文件", businessType = BusinessType.DELETE)
    @RequestMapping(value = "/deletefile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> deleteFile(@RequestBody DeleteFileDTO deleteFileDto) {
        userFileService.deleteUserFile(deleteFileDto.getUserFileId(), getUserId(),deleteFileDto.getFileDeptName());
        fileDealComp.updateESByUserFileId(deleteFileDto.getUserFileId(),1);
        return R.ok();
    }

    @Log(title = "解压文件", businessType = BusinessType.INSERT)
    @RequestMapping(value = "/unzipfile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> unzipFile(@RequestBody UnzipFileDTO unzipFileDto) {
        try {
            fileService.unzipFile(unzipFileDto.getUserFileId(), unzipFileDto.getUnzipMode(), unzipFileDto.getFilePath(), unzipFileDto.getFileDeptName());
        } catch (QiwenException e) {
            return R.fail(e.getMessage());
        }
        return R.ok();

    }

    @Log(title = "文件复制", businessType = BusinessType.INSERT)
    @RequestMapping(value = "/copyfile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> copyFile(@RequestBody CopyFileDTO copyFileDTO) {
        String userFileId = copyFileDTO.getUserFileId();
        UserFile userFile = userFileService.getById(userFileId);
        String oldfilePath = userFile.getFilePath();
        String newfilePath = copyFileDTO.getFilePath();
        String fileName = userFile.getFileName();
        if (userFile.getIsDir() == 1) {
            FilesUtils filesUtils = new FilesUtils(oldfilePath, fileName, true);
            if (newfilePath.startsWith(filesUtils.getPath() + FilesUtils.separator) || newfilePath.equals(filesUtils.getPath())) {
                return R.fail("原路径与目标路径冲突，不能复制");
            }
        }
        userFileService.userFileCopy(userFileId, newfilePath,getUserId());
        fileDealComp.deleteRepeatSubDirFile(newfilePath, getUserId(),copyFileDTO.getFileDeptName());
        return R.ok();
    }

    @Log(title = "文件移动", businessType = BusinessType.UPDATE)
    @RequestMapping(value = "/movefile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> moveFile(@RequestBody MoveFileDTO moveFileDto) {
        UserFile userFile = userFileService.getById(moveFileDto.getUserFileId());
        String oldfilePath = userFile.getFilePath();
        String newfilePath = moveFileDto.getFilePath();
        String fileName = userFile.getFileName();
        String extendName = userFile.getExtendName();
        if (StringUtil.isEmpty(extendName)) {
            FilesUtils filesUtils = new FilesUtils(oldfilePath, fileName, true);
            if (newfilePath.startsWith(filesUtils.getPath() + FilesUtils.separator) || newfilePath.equals(filesUtils.getPath())) {
                return R.fail("原路径与目标路径冲突，不能移动");
            }
        }
        userFileService.updateFilepathByUserFileId(moveFileDto.getUserFileId(), newfilePath, getUserId());
        fileDealComp.deleteRepeatSubDirFile(newfilePath, getUserId(),moveFileDto.getFileDeptName());
        return R.ok();

    }

    @Log(title = "批量文件移动", businessType = BusinessType.UPDATE)
    @RequestMapping(value = "/batchmovefile", method = RequestMethod.POST)
    @ResponseBody
    public R<String> batchMoveFile(@RequestBody BatchMoveFileDTO batchMoveFileDto) {
        String files = batchMoveFileDto.getFiles();
        String newfilePath = batchMoveFileDto.getFilePath();
        List<UserFile> fileList = JSON.parseArray(files, UserFile.class);
        for (UserFile userFile : fileList) {
            if (StringUtil.isEmpty(userFile.getExtendName())) {
                FilesUtils filesUtils = new FilesUtils(userFile.getFilePath(), userFile.getFileName(), true);
                if (newfilePath.startsWith(filesUtils.getPath() + FilesUtils.separator) || newfilePath.equals(filesUtils.getPath())) {
                    return R.fail("原路径与目标路径冲突，不能移动");
                }
            }
            userFileService.updateFilepathByUserFileId(userFile.getUserFileId(), newfilePath, getUserId());
        }
        return R.ok("批量移动文件成功");

    }


    @RequestMapping(value = "/selectfilebyfiletype", method = RequestMethod.GET)
    @ResponseBody
    public R<Map<String, Object>> selectFileByFileType(int fileType, String fileDeptName,
                                                       @RequestParam(defaultValue = "1") long currentPage,
                                                       @RequestParam(defaultValue = "10") long pageCount) {
        IPage<FileListVo> result = userFileService.getFileByFileType(fileType, fileDeptName, currentPage, pageCount, getUserId());
        Map<String, Object> map = new HashMap<>();
        map.put("list", result.getRecords());
        map.put("total", result.getTotal());
        return R.ok(map);

    }

    @RequestMapping(value = "/getfiletree", method = RequestMethod.GET)
    @ResponseBody
    public R<TreeNode> getFileTree(String fileDeptName) {
        List<UserFile> userFileList = userFileService.selectFilePathTreeByUserId(getUserId(), fileDeptName);
        TreeNode resultTreeNode = new TreeNode();
        resultTreeNode.setLabel(FilesUtils.separator);
        resultTreeNode.setId(0L);
        long id = 1;
        for (UserFile userFile : userFileList) {
            FilesUtils filesUtils = new FilesUtils(userFile.getFilePath(), userFile.getFileName(), false);
            String filePath = filesUtils.getPath();
            Queue<String> queue = new LinkedList<>();
            String[] strArr = filePath.split(FilesUtils.separator);
            for (String s : strArr) {
                if (!"".equals(s) && s != null) {
                    queue.add(s);
                }
            }
            if (queue.size() == 0) {
                continue;
            }
            resultTreeNode = fileDealComp.insertTreeNode(resultTreeNode, id++, FilesUtils.separator, queue);
        }
        List<TreeNode> treeNodeList = resultTreeNode.getChildren();
        treeNodeList.sort((o1, o2) -> {
            long i = o1.getId() - o2.getId();
            return (int) i;
        });
        return R.ok(resultTreeNode);
    }

    @Log(title = "修改文件", businessType = BusinessType.UPDATE)
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    @ResponseBody
    public R<String> updateFile(@RequestBody UpdateFileDTO updateFileDTO) {
        UserFile userFile = userFileService.getById(updateFileDTO.getUserFileId());
        FileBean fileBean = fileService.getById(userFile.getFileId());
        Long pointCount = fileService.getFilePointCount(userFile.getFileId());
        String fileUrl = fileBean.getFileUrl();
        if (pointCount > 1) {
            fileUrl = fileDealComp.copyFile(fileBean, userFile);
        }
        String content = updateFileDTO.getFileContent();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content.getBytes());
        try {
            int fileSize = byteArrayInputStream.available();
            fileDealComp.saveFileInputStream(fileBean.getStorageType(), fileUrl, byteArrayInputStream);
            String md5Str = fileDealComp.getIdentifierByFile(fileUrl, fileBean.getStorageType());
            fileService.updateFileDetail(userFile.getUserFileId(), md5Str, fileSize, getUserId(), updateFileDTO.getFileDeptName());
        } catch (Exception e) {
            throw new QiwenException(999999, "修改文件异常");
        } finally {
            try {
                byteArrayInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return R.ok("修改文件成功");
    }

    @RequestMapping(value = "/detail", method = RequestMethod.GET)
    @ResponseBody
    public R<FileDetailVO> queryFileDetail(String userFileId){
        FileDetailVO vo = fileService.getFileDetail(userFileId);
        return R.ok(vo);
    }
}
